﻿#include "singleton_app.h"

#include "util/util.h"

#ifdef CPPC_OS_WINDOWS
#include <Windows.h>
#include <winerror.h>
#else
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#endif

#include <fstream>
#include <iostream>

SingletonApp::SingletonApp(const std::string &appName, bool autoExit) :
    m_appName(appName), m_autoExit(autoExit) {
    m_appIsRunning = false;
    m_singleFileLockFD = 0;

    tryLock();

    if (m_autoExit && isRunning()) {
        exit(0);
    }
}

SingletonApp::~SingletonApp() {
#ifdef CPPC_OS_LINUX
    if (m_singleFileLockFD > 0) {
        struct flock fl;
        fl.l_type = F_UNLCK;
        fl.l_start = 0;
        fl.l_whence = SEEK_SET;
        fl.l_len = 0;

        fcntl(m_singleFileLockFD, F_SETLK, &fl);
        close(m_singleFileLockFD);
        unlink(m_appPath.c_str()); // 删除锁文件
    }
#endif
}

bool SingletonApp::tryLock() {
    if (m_appName.empty()) {
        return true;
    }

#ifdef CPPC_OS_WINDOWS
    m_appPath = cppc::format("sp_%s.lock", m_appName.c_str());
    std::string singleName = cppc::format("Global\\%s", m_appPath.c_str()); // 全局唯一
    void *singleEvent_ = (void *)::CreateEvent(NULL, false, false, singleName.c_str());

    if (!singleEvent_ || (::GetLastError() == ERROR_ALREADY_EXISTS)) {
        m_appIsRunning = true;
        return false;
    }
#else
    m_appPath ="/var/lock/sp_";
    m_appPath = m_appPath+m_appName.c_str()+".lock";
    m_singleFileLockFD = open(m_appPath.c_str(), O_RDWR | O_CREAT, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
    if (m_singleFileLockFD < 0) {
        m_appIsRunning = true;
        m_runningAppID = readProcessID();
        return false;
    }
    struct flock fl;
    fl.l_type = F_WRLCK;
    fl.l_start = 0;
    fl.l_whence = SEEK_SET;
    fl.l_len = 0;

    int ret = fcntl(m_singleFileLockFD, F_SETLK, &fl);
    if (-1 == ret) {
        m_appIsRunning = true;
        m_runningAppID = readProcessID();
        close(m_singleFileLockFD);
        m_singleFileLockFD = 0;
        return false;
    } else {
        writeProcessID();
    }
#endif
    return true;
}

bool SingletonApp::tryKillAndLock(int waitTime) {
    if (m_runningAppID <= 0 || m_runningAppID == (unsigned long long)getpid()) {
        return true;
    }
    do {
        if (kill(m_runningAppID, 0) == 0) {
            kill(m_runningAppID, SIGKILL);
            usleep(10000);
            waitTime -= 10;
        }

        bool lockResult = tryLock();
        if (lockResult) {
            return true;
        }

        usleep(10000);
        waitTime -= 10;
    } while (waitTime > 0);
    return false;
}

std::string SingletonApp::appName() const {
    return m_appName;
}

bool SingletonApp::isRunning() const {
    return m_appIsRunning;
}

bool SingletonApp::writeProcessID() {
    if (m_singleFileLockFD > 0) {
        // 移动文件指针到文件开头
        if (lseek(m_singleFileLockFD, 0, SEEK_SET) == -1) {
            return false;
        }

        // 清除文件内容
        if (ftruncate(m_singleFileLockFD, 0) == -1) {
            return false;
        }

        pid_t processID = getpid();
        std::string pidString = std::to_string(processID);
        // 获取当前进程的ID

        ssize_t bytesWritten = write(m_singleFileLockFD, pidString.c_str(), pidString.size());
        if (bytesWritten >= 0) {
            return true;
        }
    }
    return false;
}

unsigned long long SingletonApp::readProcessID() {
    if (m_singleFileLockFD > 0) {
        char buffer[256];
        ssize_t bytesRead = read(m_singleFileLockFD, buffer, sizeof(buffer) - 1);

        if (bytesRead <= 0) {
            return 0;
        } else {
            buffer[bytesRead] = '\0';
            pid_t processId = std::stoi(buffer);
            return processId;
        }
    }
    return 0;
}
